import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Base64;
import java.util.List;
import java.util.zip.*;

public class ZipDemo {

    public static String strGzip(String oStr) {
        // https://www.jianshu.com/p/b3fd26d82fd7

        if (oStr == null || oStr.length() == 0) {
            return oStr;
        }

        ByteArrayOutputStream out = new ByteArrayOutputStream();

        GZIPOutputStream gzip = null;
        try {
            gzip = new GZIPOutputStream(out);
            gzip.write(oStr.getBytes());
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (gzip != null) {
                try {
                    gzip.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

        // gzip bytes --> base64 bytes
        byte[] rtnStrBytes = Base64.getEncoder().encode(out.toByteArray());
        // base64 bytes --> gzip string
        String strRtn = new String(rtnStrBytes, StandardCharsets.UTF_8);

        return strRtn;
    }

    public static String strUngzip(String zipStr) {
        if (zipStr == null) {
            return null;
        }

        ByteArrayOutputStream out = new ByteArrayOutputStream();
        ByteArrayInputStream in = null;
        GZIPInputStream ginzip = null;
        byte[] compressed = null;
        String decompressed = null;

        try {
            // gzip string --> base64 bytes -> gzip bytes
            compressed = Base64.getDecoder().decode(zipStr);

            // gzip bytes
            in = new ByteArrayInputStream(compressed);
            ginzip = new GZIPInputStream(in);

            byte[] buffer = new byte[4096];
            int offset = -1;
            while ((offset = ginzip.read(buffer)) != -1) {
                out.write(buffer, 0, offset);
            }
            decompressed = out.toString();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (ginzip != null) {
                try {
                    ginzip.close();
                } catch (IOException e) {
                }
            }
            if (in != null) {
                try {
                    in.close();
                } catch (IOException e) {
                }
            }
            if (out != null) {
                try {
                    out.close();
                } catch (IOException e) {
                }
            }
        }

        return decompressed;
    }


    /**
     * 压缩文件方法
     *
     * @param fileList 需要压缩的文件列表
     * @param destFile 目标文件
     * @throws IOException 异常信息
     */
    public static void fileZip(List<String> filePathList, String destFilePath) throws IOException {
        if (filePathList == null || filePathList.isEmpty()) {
            return;
        }

        //zip输出流对象
        ZipOutputStream zipOutputStream = new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(new File(destFilePath))));

        //设置压缩等级，level (0-9)
        zipOutputStream.setLevel(5);

        //遍历文件列表
        for (String filePath : filePathList) {

            DateTimeDemo.logTs("fileZip "+filePath);

            File file = new File(filePath);
            //zip文件条目对象
            ZipEntry zipEntry = new ZipEntry(file.getName());
            //将文件条目对象放入zip流中
            zipOutputStream.putNextEntry(zipEntry);
            //将文件信息写入zip流中
            FileInputStream fileInputStream = new FileInputStream(file);
            byte[] bytes = new byte[4096];
            int len = 0;
            while ((len = fileInputStream.read(bytes)) != -1) {
                zipOutputStream.write(bytes, 0, len);
            }
            zipOutputStream.closeEntry();
            fileInputStream.close();
        }

        zipOutputStream.close();
    }

    /**
     * 解压缩zip文件
     *
     * @param zipFile  待解压的文件
     * @param destFile 解压到哪个目录
     */
    public static void fileUnzip(String zipFilePath, String destFilePath) throws IOException {
        File zipFile = new File(zipFilePath);
        File destFile = new File(destFilePath);

        //目标文件必须是一个目录
        if (destFile.isFile()) {
            return;
        }

        //文件夹不存在要先创建文件夹
        if (!destFile.exists()) {
            destFile.mkdirs();
        }

        //创建一个zip读取流，在try中直接写会自动关闭流对象，无需手动关闭
        try (ZipInputStream zipInputStream = new ZipInputStream(new BufferedInputStream(new FileInputStream(zipFile)))) {
            ZipEntry zipEntry;
            //遍历读取文件条目，读取下一个 ZIP文件条目并将流定位在条目数据的开头。
            while ((zipEntry = zipInputStream.getNextEntry()) != null) {
                String name = zipEntry.getName();

                DateTimeDemo.logTs("fileUnzip "+name);

                //构建一个新文件对象，创建在destFile目录下
                File f = new File(destFile, name);
                //将文件条目数据写入到新的文件对象中，
                FileOutputStream fileOutputStream = new FileOutputStream(f, false);
                byte[] bytes = new byte[4096];
                int len = 0;
                while ((len = zipInputStream.read(bytes)) != -1) {
                    fileOutputStream.write(bytes, 0, len);
                }
                zipInputStream.closeEntry();
                fileOutputStream.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    protected static void strZipDemo() {

        String oStr = """
                七律·长征
                毛泽东
                1935年10月
                红军不怕远征难，万水千山只等闲
                五岭逶迤腾细浪，乌蒙磅礴走泥丸
                金沙水拍云崖暖，大渡桥横铁索寒
                更喜岷山千里雪，三军过后尽开颜
                """;
        System.out.println("oStr: " + oStr);

        String zipStr = strGzip(oStr);
        System.out.println("zipStr: " + zipStr);

        String unzipStr = strUngzip(zipStr);
        System.out.println("unzipStr: " + unzipStr);
    }

    protected static void fileZipDemo() {

        try {
            // 获取当前类工作目录
            String workingDIRPath = FileDemo.currentWorkdingDir();
            File dir = new File(workingDIRPath); // filePath 要遍历的目录
            //System.out.println(dir);
            if (dir.isDirectory()) {
                //System.out.println("dir: " + filePath);
                // 返回
                String[] children = dir.list();
                ArrayList<String> files = new ArrayList<String>();
                for (String sub : children) {
                    if (sub.indexOf("test") != -1 || sub.indexOf("demoTest") != -1) {
                        files.add(workingDIRPath+File.separator+sub);
                    }
                }
                String zipFile = workingDIRPath+File.separator+"demoZipfile.zip";

                DateTimeDemo.logTs("before make zip, zipFile"+zipFile);
                // 将多个文件压缩成为一个文件
                fileZip(files, zipFile);

                String upzipDir = workingDIRPath+File.separator+"demoUnZipDir";
                DateTimeDemo.logTs("before make unzip, upzipDir "+upzipDir);
                fileUnzip(zipFile, upzipDir);

            }
        } catch (IOException e) {
            return;
        }
    }

    protected static void demoLogic() {
        DateTimeDemo.logTs("strZipDemo");
        strZipDemo();

        DateTimeDemo.logTs("fileZipDemo");
        fileZipDemo();
    }
}
